package chen.you.comm.log

import java.util.*
import kotlin.math.min

/**
 * author: you : 2018/12/27
 * @param logAppender 核心写入
 * @param formatter 格式化
 * @param maxAppendLength 单次日志添加时日志最大添加长度,超过时分割多段后写入
 */
class AppenderLogger private constructor(
    private val logAppender: LogAppender,
    private val formatter: LogFormatter,
    private val maxAppendLength: Int,
    bufferPath: String
) : Logger {
    //日志拦截
    private var interceptorList: ArrayList<Interceptor>? = null

    init {
        logAppender.openBuffer(bufferPath)
    }

    override fun println(level: Int, tag: String, msg: String) {
        var intercepted = false
        run outside@{
            interceptorList?.forEach {
                if (it.intercept(level, tag, msg)) {
                    intercepted = true
                    return@outside
                }
            }
        }
        if (!intercepted) {
            if (msg.length <= maxAppendLength) {
                logAppender.appender(formatter.format(level, tag, msg))
            } else {
                val msgLength = msg.length
                var start = 0
                var end = maxAppendLength
                while (start < msgLength) {
                    logAppender.appender(formatter.format(level, tag, msg.substring(start, end)))
                    start = end
                    end = min(start + maxAppendLength, msgLength)
                }
            }
        }
    }

    override fun flush() {
        logAppender.flush()
    }

    override fun release() {
        logAppender.closeBuffer()
    }

    fun addInterceptor(interceptor: Interceptor) {
        if (interceptorList == null) interceptorList = ArrayList()
        interceptorList!!.add(interceptor)
    }

    fun removeInterceptor(interceptor: Interceptor) {
        interceptorList?.remove(interceptor)
    }

    companion object {
        //最好4K 倍数
        const val DEF_BUFFERSIZE = 180 * 1024L

        //单次最大写入的日志长度
        const val DEF_APPENDER_LEN = DEF_BUFFERSIZE / 10

        //默认flush时长, 如Debug需要打印时可以小一点, 单位秒
        const val DEF_FLUSH_DELAY = 15 * 60L

        //日志保留时间
        const val DEF_LOG_ALIVE_TIME = 4 * 24 * 3600L

        //时间格式化
        const val DEF_FORMAT_TIME = "%d-%02d-%02d %02d:%02d:%02d"
    }

    /**
     * 格式化日志格式
     */
    interface LogFormatter {
        //格式化日志
        fun format(@LogUtils.Level level: Int, tag: String, msg: String): String
    }

    class DateLogFormatter(private val format: String = DEF_FORMAT_TIME) : LogFormatter {
        //在大量format时间格式时,此种方式处理速度更快
        private val currTimeStr = StringBuilder()

        //字符串格式化
        private val formatter = Formatter(currTimeStr)

        //格式日志StringBuilder
        private val logStringBuilder = StringBuilder()
        private val calendar = Calendar.getInstance()

        @Synchronized
        override fun format(level: Int, tag: String, msg: String): String {
            calendar.timeInMillis = System.currentTimeMillis()
            currTimeStr.setLength(0)
            formatter.format(
                format, calendar[Calendar.YEAR], calendar[Calendar.MONTH] + 1,
                calendar[Calendar.DAY_OF_MONTH], calendar[Calendar.HOUR_OF_DAY],
                calendar[Calendar.MINUTE], calendar[Calendar.SECOND]
            )
            logStringBuilder.setLength(0)
            return logStringBuilder.append(currTimeStr)
                .append(' ').append(LogUtils.leveChar(level))
                .append(' ').append(tag)
                .append(' ').append(msg)
                .append('\n').toString()
        }
    }

    class Builder(private val logfileDir: String, private val bufferPath: String) {

        private var isDebug = false

        private var bufferSize = 0L

        private var flushDelay = 0L

        private var maxLogSize: Long = 0L

        private var logAliveTime: Long = 0L

        private var maxAppendLength = 0

        private var formatter: LogFormatter? = null

        fun setDebug(debug: Boolean): Builder = also { it.isDebug = debug }

        fun setBufferSize(bufferSize: Long): Builder = also { it.bufferSize = bufferSize }

        fun setFlushDelay(flushDelay: Long): Builder = also { it.flushDelay = flushDelay }

        fun setMaxLogSize(maxLogSize: Long): Builder = also { it.maxLogSize = maxLogSize }

        fun setLogAliveTime(logAliveTime: Long): Builder = also { it.logAliveTime = logAliveTime }

        fun setMaxAppendLength(maxAppendLength: Int): Builder = also { it.maxAppendLength = maxAppendLength }

        fun setFormatter(formatter: LogFormatter): Builder = also { it.formatter = formatter }

        fun build(): AppenderLogger {
            if (bufferSize <= 3) bufferSize = DEF_BUFFERSIZE
            if (flushDelay <= 0) flushDelay = DEF_FLUSH_DELAY
            if (logAliveTime <= 0) logAliveTime = DEF_LOG_ALIVE_TIME
            if (maxAppendLength <= 0) maxAppendLength = (bufferSize / 3).toInt()
            return AppenderLogger(
                LogAppender(logfileDir, bufferSize, flushDelay, maxLogSize, logAliveTime, isDebug),
                formatter ?: DateLogFormatter(), maxAppendLength, bufferPath
            )
        }
    }

}